package qmlt.dataset;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.json.JSONException;

import qmlt.dataset.filter.Filter;

public class DataSetBase implements DataSet
{

	private final DataSetDef			def;

	private final DataSet					ancestor;

	private final List<Instance>	instances	= new ArrayList<Instance>();

	public DataSetBase(DataSetDef def, DataSet ancestor)
	{
		this.def = def;
		this.ancestor = ancestor;
	}

	@Override
	public String getId()
	{
		return def.id;
	}

	public DataSet getAncestor()
	{
		return ancestor;
	}
	
	@Override
	public DataSetDef getDef()
	{
		return def;
	}

	@Override
	public List<Attribute> getFeatureDefs()
	{
		return def.featureDefs;
	}

	@Override
	public Attribute getTargetDef()
	{
		return def.targetDef;
	}

	@Override
	public List<Instance> getInstances()
	{
		return instances;
	}

	@Override
	public DataSet intersection(DataSet ds, String id)
	{
		DataSet rst = new DataSetBase(def.clone(id), this);

		for (Instance i : getInstances())
		{
			for (Instance j : ds.getInstances())
			{
				if (i.id.equals(j.id))
				{
					rst.getInstances().add(i);
					break;
				}
			}
		}

		return rst;
	}

	@Override
	public DataSet union(DataSet ds, String id)
	{
		class helper
		{
			DataSet			rst;

			Set<String>	ids	= new HashSet<String>();

			public helper(DataSet rst)
			{
				this.rst = rst;
			}

			public void processDataSet(DataSet ds)
			{
				for (Instance i : ds.getInstances())
				{
					if (!ids.contains(i.id))
					{
						rst.getInstances().add(i);
						ids.add(i.id);
					}
				}
			}
		}

		DataSet rst = new DataSetBase(def.clone(id), this);
		helper h = new helper(rst);
		h.processDataSet(this);
		h.processDataSet(ds);
		return rst;
	}

	@Override
	public List<DataSet> randomSplit(String id1, double ratio1, String id2)
	{
		randomPermute();

		int size = getInstances().size();
		List<DataSet> dss = new ArrayList<DataSet>();
		dss.add(new DataSetBase(def.clone(id1), this));
		dss.add(new DataSetBase(def.clone(id2), this));

		int size1 = (int) (size * ratio1);

		int i = 0;
		while (i < size && i < size1)
		{
			Instance inst = getInstances().get(i);
			dss.get(0).getInstances().add(inst);
			i++;
		}
		while (i < size)
		{
			Instance inst = getInstances().get(i);
			dss.get(1).getInstances().add(inst);
			i++;
		}

		return dss;
	}

	@Override
	public List<DataSet> randomSplit(String id1, double ratio1, String id2, double ratio2, String id3)
	{
		randomPermute();

		int size = getInstances().size();
		List<DataSet> dss = new ArrayList<DataSet>();
		dss.add(new DataSetBase(def.clone(id1), this));
		dss.add(new DataSetBase(def.clone(id2), this));
		dss.add(new DataSetBase(def.clone(id3), this));

		int size1 = (int) (size * ratio1);
		int size2 = (int) (size * ratio2);

		int i = 0;
		while (i < size && i < size1)
		{
			Instance inst = getInstances().get(i);
			dss.get(0).getInstances().add(inst);
			i++;
		}
		while (i < size && i < size1 + size2)
		{
			Instance inst = getInstances().get(i);
			dss.get(1).getInstances().add(inst);
			i++;
		}
		while (i < size)
		{
			Instance inst = getInstances().get(i);
			dss.get(2).getInstances().add(inst);
			i++;
		}

		return dss;
	}

	@Override
	public void randomPermute()
	{
		int size = getInstances().size();
		for (int i = 0; i < size; ++i)
		{
			int j = i + (int) (Math.random() * (size - i));
			Instance tmp = getInstances().get(i);
			getInstances().set(i, getInstances().get(j));
			getInstances().set(j, tmp);
		}
	}

	@Override
	public DataSet filter(Filter filter)
	{
		return filter.filter(this);
	}
	
	public static DataSet read(String defFilePath, String dataFilePath) throws IOException,
			JSONException
	{
		DataSetDef def = DataSetDef.read(defFilePath);

		DataSet rst = new DataSetBase(def, null);

		BufferedReader br = new BufferedReader(new FileReader(dataFilePath));
		int k = 0;
		String line;
		while ((line = br.readLine()) != null)
		{
			int posSharp = line.indexOf('#');
			if (posSharp >= 0)
			{
				line = line.substring(0, posSharp);
			}
			if (line.trim().isEmpty())
				continue;

			k++;
			String[] parts = line.split(",");
			assert parts.length == def.defs.size() : "can't process line: " + line;

			Instance inst = new Instance(String.valueOf(k), rst);
			for (int i = 0; i < parts.length; ++i)
			{
				// determine value
				Object value = null;
				if (!def.defs.get(i).noValueString.equals(parts[i]))
				{
					if (def.defs.get(i).type.equals(Attribute.STRING))
					{
						value = parts[i];
					}
					else if (def.defs.get(i).type.equals(Attribute.FLOAT))
					{
						value = new Float(parts[i]);
					}
				}

				// determine where to add
				if (i == def.targetIndex)
				{
					inst.setTarget(value);
				}
				else
				{
					inst.getFeatures().add(value);
				}
			}
			rst.getInstances().add(inst);
		}
		br.close();

		return rst;
	}

}
